package interview.i_003_concurrent;

import org.junit.jupiter.api.Test;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.locks.LockSupport;

/**
 * 两个线程，t1 添加 10 个元素到容器中，t2 实现监控元素个数，到 5 个时，给出提示并结束
 */
class T004_MyContainer {
    Thread t1 = null, t2 = null;
    List<Object> list1 = new LinkedList<>();
    volatile List<Object> list2 = new LinkedList<>();
    // List<Object> list2 = Collections.synchronizedList(new LinkedList<>());

    @Test
    void demo1() throws InterruptedException { // 分析 demo1，能完成这个功能吗？
        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                list1.add(new Object());
                System.out.println("add " + i);
                try {
                    Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
                } catch (InterruptedException ignored) {
                }
            }
        });
        t1.start();
        new Thread(() -> {
            while (true) if (list1.size() == 5) break;
            System.out.println("end");
        }).start();
        t1.join();
    }

    @Test
    void demo2() throws InterruptedException { // volatile 修饰符对对象内部状态的可见性影响
        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                list2.add(new Object());
                System.out.println("add " + i);
                try {
                    Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
                } catch (InterruptedException ignored) {
                }
            }
        });
        t1.start();
        new Thread(() -> {
            while (true) if (list2.size() == 5) break;
            System.out.println("end");
        }).start();
        t1.join();
    }

    @Test
    void demo3() throws InterruptedException { // notify 不会释放锁，t1 结束时 t2 才退出
        final Object lock = new Object();
        new Thread(() -> {
            synchronized (lock) {
                if (list1.size() != 5) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ignored) {
                    }
                }
                System.out.println("end");
            }
        }).start();
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        new Thread(() -> {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    list1.add(new Object());
                    System.out.println("add " + i);
                    if (list1.size() == 5) lock.notify(); // 仅通知，不释放锁
                }
            }
        }).start();
    }

    @Test
    void demo4() throws InterruptedException {
        final Object lock = new Object();
        new Thread(() -> {
            synchronized (lock) {
                if (list1.size() != 5) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ignored) {
                    }
                }
                System.out.println("end");
                lock.notify(); // 通知 t1 继续执行
            }
        }).start();
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        new Thread(() -> {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    list1.add(new Object());
                    System.out.println("add " + i);
                    if (list1.size() == 5) {
                        lock.notify();
                        try {
                            lock.wait(); // 释放锁，让 t2 得以执行
                        } catch (InterruptedException ignored) {
                        }
                    }
                }
            }
        }).start();
    }

    @Test
    void demo5() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {
            if (list1.size() != 5) {
                try {
                    latch.await();
                } catch (InterruptedException ignored) {
                }
            }
            System.out.println("end");
        }).start();
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                list1.add(new Object());
                System.out.println("add " + i);
                if (list1.size() == 5) {
                    latch.countDown();
                    try {
                        Thread.sleep(ThreadLocalRandom.current().nextLong(500));
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });
        t1.start();
        t1.join();
    }

    @Test
    void demo6() throws InterruptedException {
        Thread t2 = new Thread(() -> {
            if (list1.size() != 5) LockSupport.park();
            System.out.println("end");
        });
        t2.start();
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                list1.add(new Object());
                System.out.println("add " + i);
                if (list1.size() == 5) {
                    LockSupport.unpark(t2);
                    try {
                        Thread.sleep(ThreadLocalRandom.current().nextLong(500));
                    } catch (InterruptedException ignored) {
                    }
                }
            }

        });
        t1.start();
        t1.join();
    }

    @Test
    void demo7() {
        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                list1.add(new Object());
                System.out.println("add " + i);
                if (list1.size() == 5) {
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }
            }
        });
        t2 = new Thread(() -> {
            LockSupport.park();
            System.out.println("end");
            LockSupport.unpark(t1);
        });
        t2.start();
        t1.start();
    }

    @Test
    void demo8() throws InterruptedException {
        Semaphore semaphore = new Semaphore(1, true);
        t1 = new Thread(() -> {
            try {
                semaphore.acquire();
                for (int i = 0; i < 10; i++) {
                    list1.add(new Object());
                    System.out.println("add " + i);
                    if (list1.size() == 5) {
                        semaphore.release();
                        try {
                            Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
                        } catch (InterruptedException ignored) {
                        }
                    }
                }
            } catch (InterruptedException ignored) {
            }
        });
        t1.start();
        new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("end");
                semaphore.release();
            } catch (InterruptedException ignored) {
            }
        }).start();
        t1.join();
    }
}